#include "gtest/gtest.h"
//1. auto   &&  decltype(auto)
#include <utility>
#include <memory>


//use:  
//      auto p = init<10>();
template<auto n> // n is a value
auto init() -> std::pair<decltype(n), decltype(n)> {
    return {n , n};
}
TEST(AutoTest, testMain){
    auto a = 10;
    ASSERT_TRUE(bool(std::is_same_v<decltype(a), int>));
    auto * aptr = &a;
    ASSERT_TRUE(bool(std::is_same_v<decltype(aptr), int *>));
    auto & aref = a;
    ASSERT_TRUE(bool(std::is_same_v<decltype(aref), int &>));

    auto d = {1, 2};
    ASSERT_TRUE(bool(std::is_same_v<decltype(d), std::initializer_list<int>>));

    auto [v, w] = std::make_pair(1, 2);
    ASSERT_TRUE(bool(std::is_same_v<decltype(v), int>));
    ASSERT_TRUE(bool(std::is_same_v<decltype(w), int>));

    auto p = init<10>();
    ASSERT_TRUE(bool(std::is_same_v<decltype(p), std::pair<int, int>>));


    switch( int i = 0; i){
        case 0:
            std::cout << i << std::endl;
            break;
        case 1:
            break;
        default:
            break;
    };

}
struct A {
    int x;
    int y;
    int z;
};
TEST(StructureTest, testMain){
    A a {.x = 11, .y = 111}; //z = 0;
    std::cout << a.x  << a.y  << a.z << std::endl;

    A b = {10, 100, 0};
    std::cout << b.x  << b.y  << b.z << std::endl;

    {
        auto [ x, y ,z ] = b;
        std::cout << x  << y  << z << std::endl;
    }
    {
        auto [ x, y, _ ] = a;
        std::cout << x  << y << std::endl;
    }

}
namespace modern_cpp {
class Object {
public:
    Object() = default;
    explicit Object(int i): i(i){
        std::cout << "Object ctor:" << i << std::endl;
    }
    ~Object(){
        std::cout << "Object dtor:" << i << std::endl;
    }

private:
    int i = 1;
};

std::unique_ptr<Object> retUniquePtr(){
    return std::make_unique<Object>();
}
}// end of namespace modern_cpp


using namespace modern_cpp;
TEST(SmartPtrTest, testMain){
    {
        auto uptr = std::make_unique<Object>(1);
        auto sptr = std::make_shared<Object>(1);
    }

    {
        std::unique_ptr<Object> uptr(new Object(2));
        std::shared_ptr<Object> sptr(new Object(2));
    }

    { //array
        auto ua_ptr = std::make_unique<Object[]>(5);
        //auto sa_ptr = std::make_shared<Object[]>(5); //C++20
    }
    {
        auto unique = retUniquePtr();
        ASSERT_TRUE(unique != nullptr);
    }
}
//ID : 3101***264913， pwd: SHedu@264913
//
// template Args...
template<typename ...Args> 
void f_args(Args&&... args){
//    std::cout << (args) ..  std::endl;
}



class Base {
public:
    Base(){
        std::cout << "Base" << std::endl;
    }
    ~Base(){
        std::cout << "~Base" << std::endl;
    }
    Base(const Base & base){
        std::cout << "Base(const Base&)" << std::endl;
    }
    Base(Base && base){
        std::cout << "Base(Base && base)" << std::endl;
    }
};
class Sub : public Base {
public:
    Sub(){
        std::cout << "Sub" << std::endl;
    }
    ~Sub(){
        std::cout << "~Sub" << std::endl;
    }
    Sub(const Sub & sub){
        std::cout << "Sub(const Sub &)" << std::endl;
    }
};
TEST(SyntaxTest, testCopyConstructor){
    //Sub s = Base(); sub = base ==> type error

    //OK
    //Sub s; 
    //Base b(s); //=>   Base(const Base&) 
    
    //Sub s;
    //Base b(std::move(s)); //=> compile error,      Sub&& =!=> Base&& 
    
    //OK
    Base b; 
    Base b2(std::move(b)); //=>   Base(Base&&) 
}


//string reference of real string
TEST(StringViewTest, testMain){
    {
        std::string_view sv("this is an string_view");
        std::cout << sv << std::endl;
    }
    {
        std::string_view msg("");  // equal to -> std::string_view msg;
        std::cout << "[" << msg << "]" << std::endl;
    }
    {
        std::string_view msg;
        std::cout << "[" << msg << "]" << std::endl;
    }
}





TEST(RightValueTest, testMain){
    const int i = 0;
    ASSERT_TRUE(bool(std::is_same_v<decltype(i), const int>));
    int a [10] = {0};
    //int a [10] = {0,};
    ASSERT_TRUE(a[1] == 0);
}

// prvalue, lvalue, xvalue;
// glvalue( lvalue + xvalue)
// rvalue (prvalue + xvalue)
int f(int i){
    return 0;
}
TEST(DecltypeTest, testValue){
    int d;  // lvalue  as entity
    ASSERT_TRUE(bool(std::is_same_v<decltype(d), int>));
    int & dref = d; // lvalue as entity
    ASSERT_TRUE(bool(std::is_same_v<decltype(dref), int&>));
    int * dptr = &d;  // lvalue as entity
    ASSERT_TRUE(bool(std::is_same_v<decltype(dptr), int*>));

    //function as entity
    using fType = int (int i);
    //f();  //f-> prvalue
    ASSERT_TRUE(bool(std::is_same_v<decltype(f), fType>));

    //auto lambda = [=](int i) -> int { return 0;};
    auto && f1 = [](int i ) -> int { return 0;}; //prvalue as entity
    decltype(f1) f2 = std::move(f1);
    ASSERT_TRUE(bool(std::is_same_v<decltype(f1), decltype(f2)>));

    int && i = std::move(d); //decltype(i) -> int &&
    ASSERT_TRUE(bool(std::is_same_v<decltype(i), int &&>));
    // const var 10 is rvalue
    ASSERT_TRUE(bool(std::is_same_v<decltype(10), int>));
    ASSERT_TRUE(bool(std::is_same_v<decltype(10), const int>) == false);

}

class DecltypeTestObject{
public:
	enum class Code {
		kOK=0,
	};
	void f(){
	}
};
TEST(DecltypeTest, testExpr){
    // text const var is expr;  and is lvalue, can &"hello"
    ASSERT_TRUE(bool(std::is_same_v<decltype("hello"), const char (&) [6]>));
	int a[10];
    ASSERT_TRUE(bool(std::is_same_v<decltype(a), int [10]>)); // array is an lvalue
    ASSERT_TRUE(bool(std::is_same_v<decltype(a[0]), int &>)); //decltype(expr) , lvalue -> T &

	int b[1][10]; 
    ASSERT_TRUE(bool(std::is_same_v<decltype(b), int [1][10]>)); // array is an lvalue
    ASSERT_TRUE(bool(std::is_same_v<decltype(b[0]), int (&)[10]>)); // array 

	int * pArr = a;
    ASSERT_TRUE(bool(std::is_same_v<decltype(pArr), int *>));
    ASSERT_TRUE(bool(std::is_same_v<decltype(pArr[0]), int &>));


	//decltype(DecltypeTestObject::Code::kOK) code;// = &DecltypeTestObject::f;	
	DecltypeTestObject obj; obj.f();
    ASSERT_TRUE(bool(std::is_same_v<decltype(obj), DecltypeTestObject>));

    ASSERT_TRUE(bool(std::is_same_v<decltype(DecltypeTestObject::Code::kOK), DecltypeTestObject::Code>));

	// use the decltype(rvalue )
	decltype(DecltypeTestObject::Code::kOK) code = DecltypeTestObject::Code::kOK;
	std::cout << static_cast<int>(code) << std::endl;	

	//xvalue
    ASSERT_TRUE(bool(std::is_same_v<decltype(std::move(DecltypeTestObject())), DecltypeTestObject &&>));

	//Object() is rvalue
    ASSERT_TRUE(bool(std::is_same_v<decltype(DecltypeTestObject()), DecltypeTestObject>));
}

//for array; 
//for container:
//for customer:
  //for range.begin() & range.end()
  //for global begin&end of range
TEST(ForLoopTest, testArray){
    int arr [] = {1, 2, 3, 4};

    for(const auto & i : arr){
        std::cout << i << " ,";
    }
    std::cout << std::endl;

    for(auto i : arr){
        std::cout << i << " ,";
    }
    std::cout << std::endl;

    for(auto && i : arr){
        std::cout << std::move(i) << " ,";
    }
    std::cout << std::endl;


    for(int i : {0, 1, 2, 3}){
        std::cout << i << " ,";
    }
    std::cout << std::endl;
}
TEST(ForLoopTest, testContainer){
    std::vector<int> v = {1, 2, 3, 4};
    //same as above
    for( auto & i : v){
        std::cout << i << "," ;
    }
    std::cout << '\n';
    //......
}


class Range {
public:
    class Iterator {
    public:
        Iterator(int value)
        :_value(value)
        {
        }
        ~Iterator(){
        }
        int operator *(){
            return _value;
        }
        int operator ++(){
            return ++_value;
        }
        bool operator != (const Iterator & right)const {
            return _value != right._value;
        }
    private:
        int _value;
    };
    Range(int start, int end)
    : _start(start)
    , _end(end)
    {
    }
    ~Range(){}
// method - 2
//    Iterator begin() const {
//        return Iterator(_start);
//    }
//    Iterator end() const {
//        return Iterator(_end);
//    }

    int start_() const{
        return _start;
    }
    int end_() const {
        return _end;
    }
private:
    int _start;
    int _end;
};

// method - 3 
Range::Iterator begin(Range & r){
    return Range::Iterator(r.start_());
}
Range::Iterator end(Range & r){
    return Range::Iterator(r.end_());
}

// for-in extention details
// for( auto & i : v):  
//      range-declaration -> auto & i ??
//      v -> __range_expr;
//
// auto && range = __range_expr;
// auto iter = range.begin();
// auto end = range.end();
// for(; iter != range.end(); ++iter ){
//      range-declaration = *iter 
// }
TEST(ForLoopTest, testObjectMember){
    for(auto i : Range(0, 10)){
        std::cout << i << ',';
    }
    std::cout << '\n';
}
